// 
// Entify project
//  
// Author:
//       Lauri Taimila <lauri@taimila.com>
// 
// Copyright (c) 2010 Lauri Taimila
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Reflection;

namespace Taimila.Entify.Sqlite
{
	/// <summary>
	/// Defines type for encapsulating all information required
	/// by SQLite for handling persistent data properites.
	/// </summary>
	internal class SqlitePersistentProperty
	{
		/// <summary>
		/// Persistent data property type.
		/// </summary>
		public Type Type
		{
			get;
			set;
		}
		
		/// <summary>
		/// Persistent data property name.
		/// </summary>
		public string Name
		{
			get;
			set;
		}
		
		/// <summary>
		/// Name used as table column name. This is seprated
		/// from proprety name to support renaming properties.
		/// </summary>
		public string PersistentName
		{
			get;
			set;
		}
		
		/// <summary>
		/// Name of the index this property should be included in.
		/// </summary>
		public string IndexName
		{
			get;
			set;
		}
		
		/// <summary>
		/// Persistent data property value.
		/// </summary>
		public object Value
		{
			get;
			set;
		}
		
		/// <summary>
		/// Is this unique persistent property.
		/// </summary>
		public bool IsUnique
		{
			get;
			set;
		}
		
		/// <summary>
		/// Is this property 'many relation' E.g. List<EntityType>
		/// </summary>
		public bool IsManyRelation
		{
			get;
			set;
		}
		
		/// <summary>
		/// SQLite parameter name of the persistent data property.
		/// </summary>
		public string SqliteParameterName
		{
		 	get
			{
				return "@param_" + this.Name.ToLower();
			}
		}
		
		/// <summary>
		/// SQLite type of the persistent data property
		/// </summary>
		public string SqliteType
		{
			get
			{
				return SqliteTypeMapper.GetSqliteColumnTypeFrom(this.Type);
			}
		}
		
		/// <summary>
		/// Value as a type that SQLiteParamter can handle.
		/// </summary>
		public object SqliteValue
		{
			get
			{
				return SqliteTypeMapper.GetValueForSqliteParameter(this.Value);	
			}
		}
		
		/// <summary>
		/// Returns string representation of the object.
		/// </summary>
		/// <returns>
		/// String representation of the object. <see cref="System.String"/>
		/// </returns>
		public override string ToString ()
		{
			return string.Format("[SqlitePersistentProperty: Type={0}, Name={1}, PersistentName={2}, IndexName={3}, Value={4}, IsUnique={5}, SqliteParameterName={6}, SqliteType={7}, SqliteValue={8}]", Type, Name, PersistentName, IndexName, Value, IsUnique, SqliteParameterName, SqliteType, SqliteValue);
		}
		
		/// <summary>
		/// Determines if this and given object are equal.
		/// </summary>
		/// <param name="obj">
		/// Object to be compared with. <see cref="System.Object"/>
		/// </param>
		/// <returns>
		/// True if equal, otherwise false. <see cref="System.Boolean"/>
		/// </returns>
		public override bool Equals(object obj)
		{
			SqlitePersistentProperty other;
			
			try
			{
				other = (SqlitePersistentProperty) obj;
			}
			catch(Exception)
			{
				return false;	
			}
			
			if(this.Name != other.Name)
			{
				return false;
			}
			
			if(this.IndexName != other.IndexName)
			{
				return false;	
			}
			
			if(this.PersistentName != other.PersistentName)
			{
				return false;
			}
			
			if(this.Type != other.Type)
			{
				return false;
			}
			
			if(this.Value != null)
			{
				if(!this.Value.Equals(other.Value))
				{
					return false;
				}
			}
			
			return true;
		}
		
		/// <summary>
		/// Overrides object.GetHashCode().
		/// </summary>
		/// <returns>
		/// object's hashcode. <see cref="System.Int32"/>
		/// </returns>
		public override int GetHashCode()
		{
			return 	this.Name.GetHashCode() ^ 
					this.PersistentName.GetHashCode() ^
				 	this.SqliteType.GetHashCode() ^ 
					this.Type.GetHashCode() ^ 
					this.Value.GetHashCode() ^ 
					this.IsUnique.GetHashCode() ^
					this.IndexName.GetHashCode();
		}
		
		/// <summary>
		/// Get a list of persistent properties from object. Returned objects
		/// have value property set from the given object.
		/// </summary>
		/// <param name="entity">
		/// Entity which persistent properties with value are returned. <see cref="System.Object"/>
		/// </param>
		/// <returns>
		/// List of persistent properties. <see cref="List<PersistentProperty>"/>
		/// </returns>
		public static List<SqlitePersistentProperty> GetPersistentPropertiesWithValues(object entity)
		{
			if(entity == null || !EntityValidator.IsValidEntityType(entity.GetType()))
			{
				throw new InvalidEntityException();	
			}
			
			Type entityType = entity.GetType();
			List<SqlitePersistentProperty> properties = new List<SqlitePersistentProperty>();

			foreach(PropertyInfo propertyInfo in entityType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
			{
				if(AttributeReader.HasEntityProperty(propertyInfo))
				{					
					SqlitePersistentProperty property = new SqlitePersistentProperty()
					{
						Name = propertyInfo.Name,
						PersistentName = AttributeReader.GetPersistentName(propertyInfo),
						Type = propertyInfo.PropertyType,
						Value = propertyInfo.GetValue(entity, null),
						IsUnique = AttributeReader.IsUnique(propertyInfo),
						IndexName = AttributeReader.GetIndexName(propertyInfo),
						IsManyRelation = AttributeReader.IsManyRelationship(propertyInfo)
					};
					
					properties.Add(property);
				}
			}
			
			return properties;
		}
		
		/// <summary>
		/// Get a list of persistent properties from type. Returned objects
		/// don't have value property set.
		/// </summary>
		/// <param name="entityType">
		/// Type which persistent properties are returned <see cref="Type"/>
		/// </param>
		/// <returns>
		/// List of persistent properties. <see cref="List<PersistentProperty>"/>
		/// </returns>
		public static List<SqlitePersistentProperty> GetPersistentProperties(Type entityType)
		{
			if(entityType == null || !EntityValidator.IsValidEntityType(entityType))
			{
				throw new InvalidEntityTypeException();	
			}
			
			List<SqlitePersistentProperty> properties = new List<SqlitePersistentProperty>();
			
			foreach(PropertyInfo propertyInfo in entityType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
			{
				if(AttributeReader.HasEntityProperty(propertyInfo))
				{
					SqlitePersistentProperty property = new SqlitePersistentProperty()
					{
						Name = propertyInfo.Name,
						PersistentName = AttributeReader.GetPersistentName(propertyInfo),
						Type = propertyInfo.PropertyType,
						Value = null,
						IsUnique = AttributeReader.IsUnique(propertyInfo),
						IndexName = AttributeReader.GetIndexName(propertyInfo),
						IsManyRelation = AttributeReader.IsManyRelationship(propertyInfo)
					};
					
					properties.Add(property);
				}
			}
			
			return properties;
		}
	}
}
